<?php

namespace Kunfeng\Dify\Client\Impl;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Kunfeng\Dify\Client\DifyBaseClientInterface;
use Kunfeng\Dify\Client\RetryHandler;
use Kunfeng\Dify\Exception\DifyApiException;
use Kunfeng\Dify\Exception\NetworkException;
use Kunfeng\Dify\Exception\ValidationException;
use Kunfeng\Dify\Model\DifyConfig;
use Kunfeng\Dify\Validator\ConfigValidator;
use Kunfeng\Dify\Model\File\FileUploadRequest;
use Kunfeng\Dify\Model\File\FileUploadResponse;
use Kunfeng\Dify\Constants\ApiPaths;

/**
 * Dify 基础客户端抽象实现
 */
abstract class AbstractDifyClient implements DifyBaseClientInterface
{
    protected $httpClient;
    protected $baseUrl;
    protected $apiKey;
    protected $retryHandler;

    public function __construct(
        string $baseUrl,
        string $apiKey,
        $httpClient = null,
        $retryHandler = null
    ) {
        $this->baseUrl = rtrim($baseUrl, '/');
        $this->apiKey = $apiKey;
        $this->httpClient = $httpClient ?? $this->createDefaultHttpClient();
        $this->retryHandler = $retryHandler ?? RetryHandler::createDefault();
    }

    /**
     * 从配置创建客户端
     */
    public static function fromConfig(DifyConfig $config)
    {
        // 验证配置
        ConfigValidator::validate($config);
        
        $httpClient = $config->httpOptions 
            ? new Client($config->httpOptions)
            : null;

        return new static($config->baseUrl, $config->apiKey, $httpClient);
    }

    /**
     * 创建默认 HTTP 客户端
     */
    protected function createDefaultHttpClient()
    {
        return new Client([
            'timeout' => 60,
            'connect_timeout' => 5,
            'headers' => [
                'Authorization' => 'Bearer ' . $this->apiKey,
                'Content-Type' => 'application/json',
                'User-Agent' => 'Dify-PHP-Client/1.0.0',
            ],
        ]);
    }

    /**
     * 执行 GET 请求
     *
     * @param string $path 请求路径
     * @param array $query 查询参数
     * @return array
     * @throws DifyApiException
     */
    protected function executeGet(string $path, array $query = []): array
    {
        return $this->retryHandler->executeWithRetry(function() use ($path, $query) {
            $response = $this->httpClient->get($this->baseUrl . $path, [
                'query' => $query
            ]);

            return $this->handleResponse($response);
        });
    }

    /**
     * 执行 POST 请求
     *
     * @param string $path 请求路径
     * @param array $data 请求数据
     * @return array
     * @throws DifyApiException
     */
    protected function executePost(string $path, array $data = []): array
    {
        return $this->retryHandler->executeWithRetry(function() use ($path, $data) {
            $response = $this->httpClient->post($this->baseUrl . $path, [
                'json' => $data
            ]);

            return $this->handleResponse($response);
        });
    }

    /**
     * 执行 PUT 请求
     *
     * @param string $path 请求路径
     * @param array $data 请求数据
     * @return array
     * @throws DifyApiException
     */
    protected function executePut(string $path, array $data = []): array
    {
        return $this->retryHandler->executeWithRetry(function() use ($path, $data) {
            $response = $this->httpClient->put($this->baseUrl . $path, [
                'json' => $data
            ]);

            return $this->handleResponse($response);
        });
    }

    /**
     * 执行 PATCH 请求
     *
     * @param string $path 请求路径
     * @param array $data 请求数据
     * @return array
     * @throws DifyApiException
     */
    protected function executePatch(string $path, array $data = []): array
    {
        return $this->retryHandler->executeWithRetry(function() use ($path, $data) {
            $response = $this->httpClient->patch($this->baseUrl . $path, [
                'json' => $data
            ]);

            return $this->handleResponse($response);
        });
    }

    /**
     * 执行 DELETE 请求
     *
     * @param string $path 请求路径
     * @param array $data 请求数据
     * @return array
     * @throws DifyApiException
     */
    protected function executeDelete(string $path, array $data = []): array
    {
        return $this->retryHandler->executeWithRetry(function() use ($path, $data) {
            $response = $this->httpClient->delete($this->baseUrl . $path, [
                'json' => $data
            ]);

            return $this->handleResponse($response);
        });
    }

    /**
     * 处理响应
     *
     * @param \Psr\Http\Message\ResponseInterface $response
     * @return array
     * @throws DifyApiException
     */
    protected function handleResponse($response): array
    {
        $statusCode = $response->getStatusCode();
        $body = $response->getBody()->getContents();
        $data = json_decode($body, true);

        if ($statusCode >= 400) {
            throw DifyApiException::fromResponse($statusCode, $data);
        }

        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new DifyApiException('Invalid JSON response: ' . json_last_error_msg());
        }

        return $data;
    }

    /**
     * 处理 Guzzle 异常
     *
     * @param GuzzleException $e
     * @return DifyApiException
     */
    protected function handleGuzzleException($e): DifyApiException
    {
        if ($e instanceof \GuzzleHttp\Exception\ClientException) {
            $response = $e->getResponse();
            $body = $response->getBody()->getContents();
            $data = json_decode($body, true) ?: [];
            
            return DifyApiException::fromResponse(
                $response->getStatusCode(),
                $data
            );
        }

        // 将其他 Guzzle 异常转换为网络异常
        return NetworkException::fromGuzzleException($e);
    }

    /**
     * 上传文件
     */
    public function uploadFile($fileOrRequest, $filePathOrStream, ?string $user = null): FileUploadResponse
    {
        // 处理不同的输入类型
        if ($fileOrRequest instanceof FileUploadRequest) {
            $request = $fileOrRequest;
        } else {
            // 从参数创建请求
            if (is_string($fileOrRequest) && file_exists($fileOrRequest)) {
                $request = FileUploadRequest::fromPath($fileOrRequest, $user);
            } elseif (is_resource($fileOrRequest)) {
                $request = FileUploadRequest::fromResource($fileOrRequest, $user);
            } else {
                throw new \InvalidArgumentException('Invalid file input');
            }
        }

        // 准备 multipart 数据
        $multipart = [
            [
                'name' => 'file',
                'contents' => $this->getFileContents($request->getFile()),
                'filename' => $request->getFileName() ?: 'file'
            ]
        ];

        if ($request->getUser()) {
            $multipart[] = [
                'name' => 'user',
                'contents' => $request->getUser()
            ];
        }

        try {
            $response = $this->httpClient->request('POST', $this->baseUrl . ApiPaths::FILES_UPLOAD, [
                'multipart' => $multipart
            ]);
            
            $data = $this->handleResponse($response);
            return FileUploadResponse::fromArray($data);
        } catch (\GuzzleHttp\Exception\GuzzleException $e) {
            throw $this->handleGuzzleException($e);
        }
    }

    /**
     * 获取文件内容
     */
    private function getFileContents($file)
    {
        if (is_string($file) && file_exists($file)) {
            return fopen($file, 'r');
        } elseif (is_resource($file)) {
            return $file;
        } else {
            throw new \InvalidArgumentException('Invalid file input');
        }
    }

    /**
     * 关闭客户端资源
     */
    public function close(): void
    {
        // 如果有需要清理的资源，在这里处理
    }
}
